#ifndef __CLX_LOOPER_H__
#define __CLX_LOOPER_H__

#include "util.hpp"
#include "buffer.hpp"
#include <mutex>
#include <thread>
#include <memory>
#include <atomic>
#include <functional>
#include <condition_variable>

namespace clxlog {
  using Functor = std::function<void(Buffer &)>;
  enum class AsynchType { ASYNCH_SAFE, ASYCH_UNSAFE };
  class AsynchLooper {
    public:
      using ptr = std::shared_ptr<AsynchLooper>;

      AsynchLooper(AsynchType looper_type, Functor callback) 
        : _stop(false), 
        _looper_type(looper_type),
        _thread(std::thread(&AsynchLooper::thread_entry, this)),
        _callback(callback) {}

      void push(const char* str, size_t len) {
        std::unique_lock<std::mutex> lock(_mtx);
        if (_looper_type == AsynchType::ASYNCH_SAFE && _produce_buffer.writeAbleSize() < len) {
          _produce_cond.wait(lock, [&](){ return _produce_buffer.writeAbleSize() >= len; });
        }
        _produce_buffer.push(str, len);
        _consumer_cond.notify_one();
      }

      void thread_entry() {
        while (1) {
          {
            // 判断 produce buffer 中有没有数据
            std::unique_lock<std::mutex> mtx(_mtx);
            if (_stop && _produce_buffer.bufferEmpty()) break;
              // 没有数据,进行阻塞等待
            _consumer_cond.wait(mtx, [&](){ return _stop || _produce_buffer.bufferEmpty(); });
            // 有数据, 则交换 buffer
            _consumer_buffer.bufferSwap(_produce_buffer);
            if (_looper_type == AsynchType::ASYNCH_SAFE) {
              _produce_cond.notify_all();
            }
          }
          // 对buffer中对数据进行落地
          _callback(_consumer_buffer);
          // 将 consumer buffer 进行清理
          _consumer_buffer.bufferReset();
        }
      }

    private:
      Buffer _produce_buffer;
      Buffer _consumer_buffer;
      std::mutex _mtx;
      std::condition_variable _produce_cond;
      std::condition_variable _consumer_cond;
      std::atomic<bool> _stop;
      AsynchType _looper_type;
      std::thread _thread;
    private:
      Functor _callback;

  };
}


#endif




